// VShadowVolume.cpp: implementation of the VShadowVolume class.
//
//////////////////////////////////////////////////////////////////////
#include <d3d9.h>
#include <d3dx9.h>
#include <d3dx9.h>
#include <D3D9.h>

#include "dxutil.h"
#include "d3dutil.h"
#include "d3dfont.h"
#include "D3DEnumeration.h"
#include "DXUtil.h"
#include "D3DEnumeration.h"
#include "D3DSettings.h"
#include "d3dutil.h"
#include <AniContainer.h>
//#include "YB_DirectX.h"
#include "VShadowVolume.h"
#include "pofTypes.h"
//#include "NarcissusApp.h"
#include "ToolApp.h"

#pragma warning(disable:4786) // turn off warning: 'identifier was truncated to 255 characters'
#include <map>
#include <list>
#include <vector>

extern ToolApp *g_Draw;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
DWORD dwNumEdges = 0;
DWORD                    m_dwNumShadowBuffers;
IDirect3DIndexBuffer9**  m_ppShadowIndexBuffer;
DWORD*                   m_pdwShadowIndexBufferSize;

IDirect3DVertexBuffer9** m_ppShadowVertexBuffer;
DWORD*                   m_pdwShadowVertexBufferSize;

typedef struct D3D_POF_SHADOW_VERTEX
{
	D3DXVECTOR3 pos;//D3DVSD_REG( 0, D3DVSDT_FLOAT3 ), // Position of first mesh
	float weight; //D3DVSD_REG( 1, D3DVSDT_FLOAT1 ), // Blend weights
	DWORD indices;//D3DVSD_REG( 2, D3DVSDT_D3DCOLOR ), // Blend indices
	D3DXVECTOR3 faceNorm;//D3DVSD_REG( 3, D3DVSDT_FLOAT3 ), // Normal
} D3D_POF_SHADOW_VERTEX;
typedef struct D3D_W_SHADOW_VERTEX
{
	D3DXVECTOR3 pos;//D3DVSD_REG( 0, D3DVSDT_FLOAT3 ), // Position of first mesh
//	D3DXVECTOR3 weight; //D3DVSD_REG( 1, D3DVSDT_FLOAT1 ), // Blend weights
	DWORD indices;//D3DVSD_REG( 2, D3DVSDT_D3DCOLOR ), // Blend indices
	D3DXVECTOR3 faceNorm;//D3DVSD_REG( 3, D3DVSDT_FLOAT3 ), // Normal
} D3D_W_SHADOW_VERTEX;

VShadowVolume::VShadowVolume()
{
	HRESULT hr;
		pFlags=NULL;
		pIndex=NULL;
		nshadow=0;
}

VShadowVolume::~VShadowVolume()
{
	if (pFlags)delete []pFlags;
	if (pIndex)delete []pIndex;

	for (int i=0;i<nshadow;i++)
	{
		shadowMesh[i]->pShVB->Release();
		shadowMesh[i]->pShIB->Release();
		delete shadowMesh[i];
	}
}
HRESULT VShadowVolume::Render( LPDIRECT3DDEVICE9 pd3dDevice )
{
	D3DXVECTOR3 light(0,1000,500);
	D3DXVECTOR3 vdir;
	D3DXVECTOR3 vnormal;
	VSHADOWVERTEX *m_pVertices;
	m_ppShadowVertexBuffer[0]->Lock( 0, 0, (VOID**)&m_pVertices, 0 );//target
	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: AddEdge()
// Desc: Adds an edge to a list of silohuette edges of a shadow volume.
//-----------------------------------------------------------------------------
VOID VAddEdge( WORD* pEdges, DWORD& dwNumEdges, WORD v0, WORD v1,D3DXVECTOR3 norm )
{
    // Remove interior edges (which appear in the list twice)
/*    for( DWORD i=0; i < dwNumEdges; i++ )
    {
        if( ( pEdges[2*i+0].ind == v0 && pEdges[2*i+1].ind == v1 ) ||
            ( pEdges[2*i+0].ind == v1 && pEdges[2*i+1].ind == v0 ) )
        {
            if( dwNumEdges > 1 )
            {
                pEdges[2*i+0].ind = pEdges[2*(dwNumEdges-1)+0].ind;
                pEdges[2*i+0].n = pEdges[2*(dwNumEdges-1)+0].n;
                pEdges[2*i+1].ind = pEdges[2*(dwNumEdges-1)+1].ind;
                pEdges[2*i+1].n = pEdges[2*(dwNumEdges-1)+1].n;
            }
            dwNumEdges--;
            return;
        }
    }

    pEdges[2*dwNumEdges+0].ind = v0;
    pEdges[2*dwNumEdges+0].n = norm;
    pEdges[2*dwNumEdges+1].ind = v1;
    pEdges[2*dwNumEdges+1].n = norm;
    dwNumEdges++;
	*/
}




//-----------------------------------------------------------------------------
// Name: BuildFromMesh()
// Desc: Takes a mesh as input, and uses it to build a VShadowVolume. The
//       technique used considers each triangle of the mesh, and adds it's
//       edges to a temporary list. The edge list is maintained, such that
//       only silohuette edges are kept. Finally, the silohuette edges are
//       extruded to make the shadow volume vertex list.
//-----------------------------------------------------------------------------
#define FLOAT_EQUALITY_FUDGE 0.001f
#define DEGEN_MERGE_THRESHOLD 1.0f  // >1.0 means no merging optimizations, -1.0 means no degen quads.

inline bool operator < (const D3DXVECTOR3& lhs, const D3DXVECTOR3& rhs)
{
   D3DXVECTOR3 d = lhs - rhs;
   const FLOAT f = FLOAT_EQUALITY_FUDGE;
   if (d.x < -f) return true;
   if (d.x > f) return false;
   if (d.y < -f) return true;
   if (d.y > f) return false;
   if (d.z < -f) return true;
   return false;
}

inline bool operator > (const D3DXVECTOR3& lhs, const D3DXVECTOR3& rhs)
{
   if (lhs == rhs) return false;
   return !(lhs < rhs);
}

class ShadowEdgePositions {
public:
   D3DXVECTOR3 p0, p1;

   bool operator == (const ShadowEdgePositions& rhs) const
   {
      return (rhs.p0 == p1 && rhs.p1 == p0);
   }
   bool operator < (const ShadowEdgePositions& rhs) const
   {
      if (p0 < rhs.p0) return true;
      if (p0 > rhs.p0) return false;
      if (p1 < rhs.p1) return true;
      return false;
   }
};

class ShadowEdgeData {
public:
   D3DXVECTOR3 n0, n1;
   WORD i0, i1;
   WORD buffer;
};
typedef std::map<ShadowEdgePositions, ShadowEdgeData> EdgeMap;


struct SKINVERT
{
	D3DXVECTOR3 pos;//D3DVSD_REG( 0, D3DVSDT_FLOAT3 ), // Position of first mesh
	D3DXVECTOR3 weight; //D3DVSD_REG( 1, D3DVSDT_FLOAT1 ), // Blend weights
	DWORD indices;//D3DVSD_REG( 2, D3DVSDT_D3DCOLOR ), // Blend indices
	D3DXVECTOR3 faceNorm;//D3DVSD_REG( 3, D3DVSDT_FLOAT3 ), // Normal
	float tu;float tv;//D3DVSD_REG( 4, D3DVSDT_FLOAT2 ), // Tex coords
};

SHADOWMESH *VShadowVolume::LoadShadowMesh( char *mesh)
{
    SHADOWMESH sMesh;
	SHADOWMESH *sh;
	sh=GetShadowMesh(mesh);
	if (sh)return sh;
	DWORD m_dwNumVertices;
	DWORD m_dwNumFaces;

	char fname[256];
	sprintf(fname,"C:\\YBDATA\\Mesh\\%s",mesh);
	FILE *fp=fopen(fname,"rb");
	if (!fp)return NULL;
	BYTE ver;
	fread(&ver,1,1,fp);
	fread(&m_dwNumVertices,4,1,fp);
	fread(&m_dwNumFaces,4,1,fp);



      // Create vertex buffer
	  HRESULT hr;
			hr=g_Draw->m_pd3dDevice->CreateVertexBuffer(m_dwNumVertices*sizeof(D3D_POF_SHADOW_VERTEX),0,
																			0,
																			D3DPOOL_MANAGED,&sMesh.pShVB,NULL);
			
      // Lock vertex buffer and fill with data
	D3D_POF_SHADOW_VERTEX* pVertex;
	sMesh.pShVB->Lock( 0, m_dwNumVertices*sizeof(D3D_POF_SHADOW_VERTEX), (VOID**)&pVertex, 0);
	fread(pVertex,m_dwNumVertices*sizeof(D3D_POF_SHADOW_VERTEX),1,fp);
      sMesh.pShVB->Unlock();

      if( FAILED( g_Draw->m_pd3dDevice->CreateIndexBuffer( m_dwNumFaces*3*sizeof(WORD), 
          0, D3DFMT_INDEX16, D3DPOOL_MANAGED , &sMesh.pShIB,NULL ) ) )
      {
         return NULL;
      }
      // Lock index buffer and fill with data
      WORD* pIndex;
      sMesh.pShIB->Lock( 0, m_dwNumFaces*3*sizeof(WORD), (VOID**)&pIndex, 0);
	  fread(pIndex,m_dwNumFaces*3*sizeof(WORD),1,fp);
     sMesh.pShIB->Unlock();

   strcpy(sMesh.name,mesh);
   sMesh.m_dwNumFaces=m_dwNumFaces;
   sMesh.m_dwNumVertices=m_dwNumVertices;
   fread(&sMesh.nComp,4,1,fp);
   fread(sMesh.BoneComp,50,1,fp);
	shadowMesh[nshadow]=new SHADOWMESH;
	strcpy(shadowMesh[nshadow]->name,mesh);
	memcpy(shadowMesh[nshadow],&sMesh,sizeof(SHADOWMESH));
	nshadow++;
	SHADOWMESH *retMesh;
	retMesh=shadowMesh[nshadow-1];
	return retMesh;

}

SHADOWMESH * VShadowVolume::GetShadowMesh(char *str)
{
//	_strupr(str);
	for (int i=0;i<nshadow;i++)
	{
		if (strcmp(str,shadowMesh[i]->name)==0)return shadowMesh[i];
	}
	return NULL;
}
SHADOWMESH *VShadowVolume::LoadShadowMeshMDL( char *mesh)
{
    SHADOWMESH sMesh;
	SHADOWMESH *sh;
	sh=GetShadowMesh(mesh);
	if (sh)return sh;
    // Note: the MESHVERTEX format depends on the FVF of the mesh
	DWORD m_dwNumVertices;
	DWORD m_dwNumFaces;
    DWORD dwNumVertices;
    DWORD dwNumFace;
	DWORD m_numFaceLists=1;
//    VSHADOWVERTEX* pVertices;

    // Lock the geometry buffers



	char fname[256];
	sprintf(fname,"C:\\YBDATA\\Mesh\\%s",mesh);
	FILE *fp=fopen(fname,"rb");
	if (!fp)return NULL;
	int nbone;

	fseek(fp,40+sizeof(D3DMATERIAL9),SEEK_SET);
	fread(&dwNumVertices,4,1,fp);
	fread(&dwNumFace,4,1,fp);

    MESHVERTEX * pVertices=new MESHVERTEX [dwNumVertices];
    WORD*       pIndices=new WORD[dwNumFace*3];


		fread(pVertices,sizeof(MESHVERTEX )*dwNumVertices,1,fp);
		fread(pIndices,sizeof(WORD)*3*dwNumFace,1,fp);
		fclose(fp);


   POFFaceChunk     face;
   POFVertexChunk   vtx;
   POFNormalChunk   norm;
   WORD *indices;
   DWORD listIdx, faceIdx, vtxIdx, edgeIdx;
   DWORD totalSourceVerts = 0;
   DWORD totalShadowVerts = 0;
   DWORD* pVtxRemap;

   D3DXVECTOR3* verts;
   D3DXVECTOR3* norms;
   
   EdgeMap edges;
   EdgeMap::iterator edgeIter;
   DWORD savedEdges = 0;

   // Allocate memory for the geometry lists.
   m_pdwShadowIndexBufferSize = new DWORD[m_numFaceLists];
   m_pdwShadowVertexBufferSize = new DWORD[m_numFaceLists];
   m_ppShadowIndexBuffer = new IDirect3DIndexBuffer9*[m_numFaceLists];
   m_ppShadowVertexBuffer = new IDirect3DVertexBuffer9*[m_numFaceLists];
   m_dwNumShadowBuffers = m_numFaceLists;

   std::vector<D3D_W_SHADOW_VERTEX>* shadowVerts = new std::vector<D3D_W_SHADOW_VERTEX>[m_numFaceLists];
   std::vector<WORD>* shadowIndices = new std::vector<WORD>[m_numFaceLists];
   D3D_W_SHADOW_VERTEX * pVertex;
   WORD* pIndex;

   // Step through all face lists to find edges and create degenerate quads.
   for (listIdx=0; listIdx < m_numFaceLists; listIdx++)
   {
      // Get D3D mesh optimization remap information
      pVtxRemap = NULL;

      // Get the face lists from the POF file
      indices = (WORD*)pIndices;

      // Step through all faces in list
      for(faceIdx=0; faceIdx < dwNumFace; faceIdx++)
      {
         // Calculate face normal
         D3DXVECTOR3 faceNorm;
         D3DXVec3Cross( &faceNorm, &(pVertices[indices[faceIdx*3+1]].pos - pVertices[indices[faceIdx*3]].pos), 
                                       &(pVertices[indices[faceIdx*3+2]].pos - pVertices[indices[faceIdx*3]].pos) );
         D3DXVec3Normalize(&faceNorm, &faceNorm);

         // Create the vertices for the face
         D3D_W_SHADOW_VERTEX shadowVert[3];
         for (vtxIdx = 0; vtxIdx < 3; vtxIdx++)
         {
             shadowVert[vtxIdx].pos = pVertices[indices[faceIdx*3+vtxIdx]].pos;
             shadowVert[vtxIdx].faceNorm= faceNorm;
			 shadowVert[vtxIdx].indices=0;
/*			 shadowVert[vtxIdx].weight.x=1.0f;
			 shadowVert[vtxIdx].weight.y=0.0f;
			 shadowVert[vtxIdx].weight.z=0.0f;*/
//             shadowVert[vtxIdx].VertNorm = pVertices[indices[faceIdx*3+vtxIdx]].VertNorm;
//             shadowVert[vtxIdx].VertNorm = faceNorm;
         }

         // Create and search edges to see which edges have a partner
         ShadowEdgePositions edgePos[3];
         ShadowEdgeData sharedEdgeDat[3];
         BOOL sharedEdgeFound[3];
         BOOL insertQuad[3];
         for (edgeIdx = 0; edgeIdx < 3; edgeIdx++)
         {
            edgePos[edgeIdx].p0 = shadowVert[edgeIdx].pos;
            edgePos[edgeIdx].p1 = shadowVert[(edgeIdx+1)%3].pos;
            ShadowEdgePositions edgePosFind;
            edgePosFind.p0 = edgePos[edgeIdx].p1;
            edgePosFind.p1 = edgePos[edgeIdx].p0;
            edgeIter = edges.find(edgePosFind);
            // If a matching edge is found, mark it as found and add it to the list of up to 3 edges for this face.
            if (edgeIter != edges.end())
            {
               sharedEdgeDat[edgeIdx] = edgeIter->second;
               sharedEdgeFound[edgeIdx] = TRUE;
               edges.erase(edgeIter);

               // If edge has a partner, see if it can be optimized away.
               insertQuad[edgeIdx] = (D3DXVec3Dot(&faceNorm, &sharedEdgeDat[edgeIdx].n0) < DEGEN_MERGE_THRESHOLD);
               if (insertQuad[edgeIdx] == FALSE) {
                  savedEdges++;
               }

               // if these verts come from a different buffer, they must be recreated in this one
               if (sharedEdgeDat[edgeIdx].buffer != listIdx)
               {
                  shadowVerts[listIdx].push_back(shadowVerts[sharedEdgeDat[edgeIdx].buffer][sharedEdgeDat[edgeIdx].i0]);
                  shadowVerts[listIdx].push_back(shadowVerts[sharedEdgeDat[edgeIdx].buffer][sharedEdgeDat[edgeIdx].i1]);
                  sharedEdgeDat[edgeIdx].i0 = shadowVerts[listIdx].size()-2;
                  sharedEdgeDat[edgeIdx].i1 = shadowVerts[listIdx].size()-1;
                  sharedEdgeDat[edgeIdx].buffer = listIdx;
               }

            }
            else
               sharedEdgeFound[edgeIdx] = FALSE;
         }

         // Figure out which vertices need to be created, and which are shared
         DWORD index[3];
         for (vtxIdx = 0; vtxIdx < 3; vtxIdx++)
         {
            // find two neighboring edges to this vertex.
            edgeIdx = vtxIdx;
            DWORD edgeIdx2 = (edgeIdx+2)%3;
            if (sharedEdgeFound[edgeIdx] == TRUE && insertQuad[edgeIdx] == FALSE)
            {
               // if the other side of the edge on one side is already found, and it is optimized away 
               // because it is coplanar, no extra vertex is needed. Just copy the index from the other face.
               index[vtxIdx] = sharedEdgeDat[edgeIdx].i1;
            } else if (sharedEdgeFound[edgeIdx2] == TRUE && insertQuad[edgeIdx2] == FALSE) {
               // if the other side of the other edge is already found, and it is optimized away 
               // because it is coplanar, no extra vertex is needed. Just copy the index from the other face.
               index[vtxIdx] = sharedEdgeDat[edgeIdx2].i0;
            } else {
               // insert a new vertex to go with this face because it is a new vertex, or it needs an invisible quad inserted.
               index[vtxIdx] = shadowVerts[listIdx].size();
               shadowVerts[listIdx].push_back(shadowVert[vtxIdx]);
            }
         }

         // Insert Face into index list
         for (vtxIdx = 0; vtxIdx < 3; vtxIdx++)
            shadowIndices[listIdx].push_back(index[vtxIdx]);

         // Insert invisible tris and quads into index list.
         for (edgeIdx = 0; edgeIdx < 3; edgeIdx++)
         {
            ShadowEdgeData edgeDat;
            edgeDat.i0 = index[edgeIdx];
            edgeDat.i1 = index[(edgeIdx+1)%3];
            edgeDat.buffer = listIdx;
            edgeDat.n0 = faceNorm;
            if (sharedEdgeFound[edgeIdx] == FALSE)
            {
               edges[edgePos[edgeIdx]] = edgeDat;
            }
            else
            {
               if (sharedEdgeDat[edgeIdx].i1 != edgeDat.i0 && sharedEdgeDat[edgeIdx].i0 != edgeDat.i1)
               {
                  // insert quad
                  shadowIndices[listIdx].push_back(edgeDat.i1);
                  shadowIndices[listIdx].push_back(edgeDat.i0);
                  shadowIndices[listIdx].push_back(sharedEdgeDat[edgeIdx].i0);

                  shadowIndices[listIdx].push_back(sharedEdgeDat[edgeIdx].i1);
                  shadowIndices[listIdx].push_back(sharedEdgeDat[edgeIdx].i0);
                  shadowIndices[listIdx].push_back(edgeDat.i0);
               } 
               else if (sharedEdgeDat[edgeIdx].i1 != edgeDat.i0)
               {
                  // insert tri collapsed on one side.
                  shadowIndices[listIdx].push_back(edgeDat.i1);
                  shadowIndices[listIdx].push_back(edgeDat.i0);
                  shadowIndices[listIdx].push_back(sharedEdgeDat[edgeIdx].i1);
               }
               else if (sharedEdgeDat[edgeIdx].i0 != edgeDat.i1)
               {
                  // insert tri collasped on the other side.
                  shadowIndices[listIdx].push_back(edgeDat.i1);
                  shadowIndices[listIdx].push_back(edgeDat.i0);
                  shadowIndices[listIdx].push_back(sharedEdgeDat[edgeIdx].i0);
               }
            }
         }
      }
   }

   // Create vertex and index buffers from calculated lists.
   for (listIdx=0; listIdx < m_numFaceLists; listIdx++)
   {
      if (shadowIndices[listIdx].size() >= 0x10000)
         OutputDebugString("Shadow Volume exceeded 0x10000 indices.\n");
      if (shadowVerts[listIdx].size() >= 0x10000)
         OutputDebugString("Shadow Volume exceeded 0x10000 vertices.\n");

      m_pdwShadowIndexBufferSize[listIdx] = shadowIndices[listIdx].size();
      m_pdwShadowVertexBufferSize[listIdx] = shadowVerts[listIdx].size();

      totalShadowVerts += m_pdwShadowVertexBufferSize[listIdx];

      // Create index buffer
      if( FAILED( g_Draw->m_pd3dDevice->CreateIndexBuffer( m_pdwShadowIndexBufferSize[listIdx]*sizeof(WORD), 
          0, D3DFMT_INDEX16, D3DPOOL_MANAGED , &m_ppShadowIndexBuffer[listIdx],NULL ) ) )
      {
         return NULL;
      }

      // Lock index buffer and fill with data
//      WORD* pIndex;
      std::vector<WORD>::iterator indexIter;
      m_ppShadowIndexBuffer[listIdx]->Lock( 0, m_pdwShadowIndexBufferSize[listIdx]*sizeof(WORD), (VOID**)&pIndex, 0);
      for (indexIter = shadowIndices[listIdx].begin(), faceIdx=0; indexIter != shadowIndices[listIdx].end(); ++indexIter, faceIdx++)
      {
         pIndex[faceIdx] = *indexIter;
      }
//      m_ppShadowIndexBuffer[listIdx]->Unlock();

      // Create vertex buffer
	  HRESULT hr;
		hr=g_Draw->m_pd3dDevice->CreateVertexBuffer(m_pdwShadowVertexBufferSize[listIdx]*sizeof(D3D_W_SHADOW_VERTEX),0,
																		0,D3DPOOL_MANAGED,&m_ppShadowVertexBuffer[listIdx],NULL);

      // Lock vertex buffer and fill with data
      std::vector<D3D_W_SHADOW_VERTEX>::iterator vertexIter;
      m_ppShadowVertexBuffer[listIdx]->Lock( 0, m_pdwShadowVertexBufferSize[listIdx]*sizeof(D3D_W_SHADOW_VERTEX), (VOID**)&pVertex, 0);
      for (vertexIter = shadowVerts[listIdx].begin(), faceIdx=0; vertexIter != shadowVerts[listIdx].end(); ++vertexIter, faceIdx++)
      {
         pVertex[faceIdx] = *vertexIter;
      }
//      m_ppShadowVertexBuffer[listIdx]->Unlock();
   }


   if (edges.size() > 0)
   {
	   int nvert=m_pdwShadowVertexBufferSize[0];
	   int nindex=m_pdwShadowIndexBufferSize[0];
      OutputDebugString("Warning: geometry is not a closed volume.\n");
//	  MessageBox(GetActiveWindow(),fname,NULL,MB_OK);
      for (edgeIter = edges.begin(), listIdx=0; edgeIter != edges.end(); ++edgeIter, listIdx++)
      {
		  ShadowEdgePositions AA;
		  ShadowEdgeData BB;
		  EdgeMap edgedata;
         ShadowEdgePositions edgePos;
         edgePos = edgeIter->first;
		 AA = edgeIter->first;
		 BB = edgeIter->second;
      }
	  m_pdwShadowVertexBufferSize[0]=nvert;
	   m_pdwShadowIndexBufferSize[0]=nindex;
   }
   m_ppShadowVertexBuffer[0]->Unlock();
   m_ppShadowIndexBuffer[0]->Unlock();

   m_dwNumVertices=m_pdwShadowVertexBufferSize[0];
   m_dwNumFaces=m_pdwShadowIndexBufferSize[0]/3;


//   _strupr(mesh);
   strcpy(sMesh.name,mesh);
   sMesh.m_dwNumFaces=m_dwNumFaces;
   sMesh.m_dwNumVertices=m_dwNumVertices;
   sMesh.pShIB=m_ppShadowIndexBuffer[0];
   sMesh.pShVB=m_ppShadowVertexBuffer[0];
	shadowMesh[nshadow]=new SHADOWMESH;
	memcpy(shadowMesh[nshadow],&sMesh,sizeof(SHADOWMESH));
	nshadow++;


   delete [] shadowVerts;
   delete [] shadowIndices;

    delete []pVertices;
    delete []pIndices;
	SHADOWMESH *retMesh;
	retMesh=shadowMesh[nshadow-1];
   return retMesh;

}
